package com.tucao.gateway.filter;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.tucao.gateway.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
 * @version 1.0
 * @Author: linshouqiang
 * @Description: 全局拦截
 * @Date: Created in 15:09 2021/1/11
 */
@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 无需进行验证的url
     */
    private List<String> skipAuthUrls = new ArrayList<String>(){
        {
            add(0,"/login");
            add(1,"/logout");
            add(2,"/register");
        }
    };

    /**
     * @Author: linshouqiang
     * @param exchange
     * @param chain
     * @return Mono<Void>
     * @Description: 全局拦截器
     * @Date: 17:59 2021/1/26
     * @version 1.0
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取请求的url
        String url = exchange.getRequest().getURI().getPath();
        log.info("url:" + url);

        //跳过不需要验证的路径
        if(!CollectionUtils.isEmpty(skipAuthUrls) && skipAuthUrls.contains(url)){
            log.info("url:{},无需验证",url);
            return chain.filter(exchange);
        }

        //获取请求头中的token
        String token = null;
        try {
            token = exchange.getRequest().getHeaders().get("token").get(0);
        } catch (Exception e) {
            //token为空，返回登录界面
            log.error("获取token失败，token为空",e);
            ServerHttpResponse response = this.setHeader(exchange.getResponse());
            return response.setComplete();
        }

        //验证token
        if (!StringUtils.isEmpty(token) && !"null".equals(token)) {
            try {
                log.info("token : {}",token);
                JWTUtils.verify(token);
                //放行
                return chain.filter(exchange);
            } catch (SignatureVerificationException e) {
                log.error("无效签名", e);
            } catch (TokenExpiredException e) {
                log.error("token过期", e);
            } catch (AlgorithmMismatchException e) {
                log.error("token算法不一致", e);
            } catch (Exception e) {
                log.error("令牌认证异常", e);
            }

            log.error("token验证失败");
            ServerHttpResponse response = this.setHeader(exchange.getResponse());
            return response.setComplete();

        } else {
            //token为空或空串，返回登录界面
            log.error("token为空串");
            ServerHttpResponse response = this.setHeader(exchange.getResponse());
            return response.setComplete();
        }

    }

    /**
     * @Author: linshouqiang
     * @return int
     * @Description: 给过滤器设置优先级 值越小优先级越高
     * @Date: 16:56 2021/1/11
     * @version 1.0
     */
    @Override
    public int getOrder() {
        return -100;
    }

    /**
     * @Author: linshouqiang
     * @param response
     * @return ServerHttpResponse
     * @Description: token不存在或过期 设置返回给前端请求头 通知其重定向到登陆界面
     * @Date: 19:14 2021/1/16
     * @version 1.0
     */
    private ServerHttpResponse setHeader(ServerHttpResponse response){
        log.info("token过期或token获取失败，提示前端重定向到登陆界面");

        HttpHeaders headers = response.getHeaders();
        headers.add("Content-Type", "text/plain;charset=UTF-8");

        //加上这一句ajax才能正常获取Authorization
        headers.set("Access-Control-Expose-Headers","token");
        headers.add("token","overdue");


        return response;
    }

}
